﻿#region License
// /* **************************************************************
// * Copyright (C) intex Consulting GmbH - All Rights Reserved    *
// *--------------------------------------------------------------*
// * Unauthorized copying of this file, via any medium is         *
// * strictly prohibited                                          *
// *--------------------------------------------------------------*
// * Proprietary and confidential                                 *
// *--------------------------------------------------------------*
// * Dissasembling/modifications strictly prohibited!             *
// ***************************************************************/
#endregion

using System;
using System.Diagnostics;
using System.Text;
using ComHlp.ContentClasses.VirtualControls.LView;
using ComHlp.enums.Fields;

namespace ComHlp.Classes.Parser.Supplementary
{
    internal class ListViewParser
    {
        /// <summary> Request listview for given map and add it to formcache - if exist </summary>
        /// <param name="fdLvInit"></param>
        /// <param name="lvResponse"></param>
        /// <returns></returns>
        internal ItxListViewContainer ParseListView(ItxListViewContainer fdLvInit, string lvResponse)
        {
            // transfer given listviewcontainer
            ItxListViewContainer fdLvCont = null;
            if (fdLvInit != null)
                fdLvCont = fdLvInit;

            // invalid response - don't display listview
            if (lvResponse.Length == 30)
            {
                //fdLvCont = new ItxListViewContainer {Display = false};
                return null; // do not return initial, cause it would replace cached formdef (reference style in calling method)
            }

            fdLvCont = SplitListViewHeader(lvResponse, fdLvCont);
            fdLvCont.Display = true;
            return fdLvCont;
        }

        /// <summary> Parse the formdef listview </summary>
        /// <param name="recvStr"></param>
        /// <param name="lvContParam"></param>
        /// <returns></returns>
        private ItxListViewContainer SplitListViewHeader(string recvStr, ItxListViewContainer lvContParam)
        {
            StringBuilder sbRecvStr = new StringBuilder(recvStr);
            // init return value
            ItxListViewContainer itxLvCont;
            if (lvContParam != null)
            {
                itxLvCont = lvContParam;
                // clear old elements
                foreach (ItxListView fdLv in lvContParam.ListViews)
                {
                    fdLv.ZColumn = null;
                    fdLv.Columns.Clear();
                }
            }
            else
                itxLvCont = new ItxListViewContainer();

            // length+15bytes trash inside the header
            sbRecvStr.Remove(0, 20);

            int columnPosition = 1;
            while (sbRecvStr.Length > 0)
            {
                // at the very first, we need the number of the listview
                int listViewNo = ParseHelper.String2Int(sbRecvStr.ToString(0, 1));
                int length = ParseHelper.String2Int(sbRecvStr.ToString(1, 3));
                char type = sbRecvStr[4];

                // look up existing listview for adding parsed parameter
                ItxListView existingLvNullable = itxLvCont.ListViews.Find(lv => lv.ListViewNo == listViewNo);
                
                // clean up 'head' string
                sbRecvStr.Remove(0, 5);

                // the z element is the very spezial last column of a table and is parse completly different
                if (type == 'Z')
                {
                    ItxListViewColumn lvColumn = new ItxListViewColumn
                        {
                            ListViewNo = listViewNo,
                            Length = length,
                            Type = type
                        };
                    ParseZElement(sbRecvStr, itxLvCont, listViewNo, existingLvNullable, lvColumn);
                }
                /* color hints for columns: [1005C0201]1005C08011005C1201
                            1 = listview no
                          005 = length
                            C = type (right here)
                           08 = column no
                           11 = color no */
                else if (type == 'C')
                {
                    int rowNo = ParseHelper.String2Int(sbRecvStr.ToString(0, 2));
                    int colorNo = ParseHelper.String2Int(sbRecvStr.ToString(2, 2));
                    ItxListViewRowColor rowColor = new ItxListViewRowColor(rowNo, colorNo);
                    existingLvNullable.RowColors.Add(rowColor);

                    // cut (but exclude type)
                    sbRecvStr.Remove(0, length-1);
                }
                // normal column 
                else
                {
                    ItxListViewColumn lvColumn = new ItxListViewColumn
                        {
                            ListViewNo = listViewNo,
                            Length = length,
                            Type = type
                        };
                    columnPosition = ParseNormalColumn(sbRecvStr, itxLvCont, columnPosition, listViewNo, existingLvNullable, lvColumn);
                }
            }
            return itxLvCont;
        }

        private int ParseNormalColumn(StringBuilder sbRecvStr, ItxListViewContainer fdLvCont, int columnPosition, int listViewNo, ItxListView existingLvNullable, ItxListViewColumn lvColumn)
        {
            lvColumn.ColumnPosition = columnPosition++;
            lvColumn.Ds12Type = ParseColumnType(sbRecvStr);
            lvColumn.Ds12TypeChar = sbRecvStr[0];
            lvColumn.PreComma = ParseHelper.String2Int(sbRecvStr.ToString(1, 2));

            // only add postcomma if type is not Date or time (Zeit)
            lvColumn.PostComma = ParsePostComma(sbRecvStr, lvColumn.Ds12Type);

            // following, we have to be carefulm cause we never get the same length...
            if (sbRecvStr.Length > 4)
                lvColumn.Ds12Aufb = sbRecvStr.ToString(4, 2);
            if (sbRecvStr.Length > 5)
                lvColumn.Ds12DisplayVerticalLine = sbRecvStr[6] == 'J';

            // if longer than 12 chars, we got a ds12 sort and column header
            if (sbRecvStr.Length > 7)
            {
                ParseDs12SortKz(sbRecvStr[7], lvColumn);

                // 2 chars blank now
                if (lvColumn.Length > 6)
                {
                    // cut the stringbuilder, that the text will start directly
                    sbRecvStr.Remove(0, 7);

                    string columnValue;
                    if (ComStatics.UseUtf8)
                        columnValue = ParseHelper.ExtractUtf8(lvColumn.Length - 8, sbRecvStr, true);
                    else
                    {
                        columnValue = sbRecvStr.ToString(0, lvColumn.Length - 8);
                        sbRecvStr.Remove(0, lvColumn.Length - 8);
                    }

                    columnValue = CheckForIconColumn(lvColumn, columnValue);
                    columnValue = CheckForRgbColumn(lvColumn, columnValue);
                    columnValue = CheckForLvColumnAction(lvColumn, columnValue);
                    columnValue = CheckLvTextForAdditionalHint(existingLvNullable, lvColumn, columnValue);
                    lvColumn.Text = columnValue.Trim();
                }
                else
                    sbRecvStr.Remove(0, lvColumn.Length - 1);
            }
            else
                sbRecvStr.Remove(0, lvColumn.Length - 1);

            // check if listview with specified No allready exist
            if (existingLvNullable != null)
            {
                existingLvNullable.Columns.Add(lvColumn);

                // handle additional global listview editable flag
                if (lvColumn.AllowActions && !existingLvNullable.AllowActions)
                    existingLvNullable.AllowActions = true;
            }
            else
            {
#if DEBUG
                // analyze this - we should never ever end up here...
                // but what do I now...
                Debugger.Break();
#endif
                ItxListView newLv = new ItxListView(-1) { ListViewNo = listViewNo };
                newLv.Columns.Add(lvColumn);
                fdLvCont.ListViews.Add(newLv);

                // handle additional global listview editable flag
                if (lvColumn.AllowActions && !newLv.AllowActions)
                    newLv.AllowActions = true;
            }

            return columnPosition;
        }

        private void ParseZElement(StringBuilder sbRecvStr, ItxListViewContainer fdLvCont, int listViewNo, ItxListView existingLvNullable, ItxListViewColumn lvColumn)
        {
            /* set paging functions (left/right/up/down) for 6 listviews
                                    parsed until Z position up to here 
                                    [1012Z 120      J] 
                                    the -1 is because we already removed the type in advance */
            lvColumn.Text = sbRecvStr.ToString(0, lvColumn.Length - 1);
            sbRecvStr.Remove(0, lvColumn.Length - 1);

            // hyperinfo?
            if (lvColumn.Text[lvColumn.Text.Length - 1] == 'J')
                lvColumn.ZColHyperInfo = true;
#if DEBUG
            // analyse circumstances we reach here and write doc
#endif

            // existing listview? - should never ever happen!
            if (existingLvNullable != null)
                existingLvNullable.ZColumn = lvColumn;
            else
            {
                ItxListView fdLv = new ItxListView(-1) { ZColumn = lvColumn, ListViewNo = listViewNo };
                fdLvCont.ListViews.Add(fdLv);
            }
        }


        /// <summary> icon inside column? </summary>
        /// <param name="lvColumn"></param>
        /// <param name="columnValue"></param>
        /// <returns></returns>
        private string CheckForIconColumn(ItxListViewColumn lvColumn, string columnValue)
        {
            /* "!!ICON   ICON/STA??$06BNR=01" | "!!ICON   ICON/RET??$05VM=RE" | "   KUNDNA??Kd-Name"
                                         * "   AUFART??$05VM=ANAA" | "   AUFKRS??" | "   AUFLFD??$05VM=K Auftrag" */
            if (!columnValue.StartsWith("   !!ICON"))
                return columnValue;

            lvColumn.IsIconColumn = true;
            // remove !!ICON
            columnValue = columnValue.Remove(0, 9);
            return columnValue;
        }

        /// <summary> rgb color for display inside column?</summary>
        /// <param name="lvColumn"></param>
        /// <param name="columnValue"></param>
        /// <returns></returns>
        private string CheckForRgbColumn(ItxListViewColumn lvColumn, string columnValue)
        {
            /* "   !!RGB   RGBCOLOR??RGB" */
            if (!columnValue.StartsWith("   !!RGB"))
                return columnValue;

            lvColumn.IsRgbColumn = true;
            // remove !!ICON
            columnValue = columnValue.Remove(0, 8);
            return columnValue;
        }

        /// <summary> check if we have received a sort or icon hint in the column header 
        /// additional if actions are allowed on this column </summary>
        /// <param name="lvColumn"></param>
        /// <param name="columnValue"></param>
        /// <returns></returns>
        private string CheckForLvColumnAction(ItxListViewColumn lvColumn, string columnValue)
        {
            /* sortable columns are indicated by '??' f.x.: 
            *   1029U 080  J      KUNDNA??Kd-Name   - display header is kd-name - sortable and sort field to server is KUNDNA
            *   1022U 020         AUFKRS??          - display header is empty but sortable with sort field AUFKRS
            *   1037UN060         AUFLFD??$05VM=K Auftrag   - display header is auftrag, sort field is auflfd and default vm is K */
            //
            // check if we have got a fieldname/icon hint, indicated through ?? inside the headline string
            // (fieldname will be in front of the ??)
            // "   AUFART??$05VM=ANAA"
            int qstMarkPos = columnValue.IndexOf("??", StringComparison.Ordinal);
            if (qstMarkPos > -1)
            {
                // check if we have got text before the ??
                if (qstMarkPos > 0)
                    lvColumn.SortFieldOrIconHint = columnValue.Substring(0, qstMarkPos).Trim();

                // set column to sortable
                lvColumn.AllowActions = true;

                // remove the ?? from the text
                columnValue = columnValue.Remove(0, qstMarkPos + 2);
            }

            return columnValue;
        }

        /// <summary> check listview column header text for additional hint 
        /// and return columnvalue in new length when hint was found </summary>
        /// <param name="existingLvNullable"></param>
        /// <param name="lvColumn"></param>
        /// <param name="columnValue"></param>
        /// <returns></returns>
        private string CheckLvTextForAdditionalHint(ItxListView existingLvNullable, ItxListViewColumn lvColumn, string columnValue)
        {
            /* sortable columns are indicated by '??' f.x.: 
            *   1029U 080  J      KUNDNA??Kd-Name   - display header is kd-name - sortable and sort field to server is KUNDNA
            *   1022U 020         AUFKRS??          - display header is empty but sortable with sort field AUFKRS
            *   1037UN060         AUFLFD??$05VM=K Auftrag   - display header is auftrag, sort field is auflfd and default vm is K */

            // check for $ sign
            // "$05VM=ANAA"
            int addHintPos = columnValue.IndexOf('$');
            while (addHintPos > -1)
            {
                // get vm length (comes after the $ sign and is 2 chars long
                int addHintLength = ParseHelper.String2Int(columnValue.Substring(addHintPos + 1, 2));
                if (addHintLength <= 0)
                    return columnValue;

                // trimstart cause we could receive spaces in front...
                columnValue = columnValue.TrimStart().Substring(3);

                // we need to check if we exceed the length....
                if (columnValue.Length < addHintLength)
                    addHintLength = columnValue.Length;

                // get the hint for analysation
                string hint = columnValue.Substring(0, addHintLength);

                // let us determine which type of hint is inside
                if (hint.Contains("VM="))// "VM=AN"
                {
                    lvColumn.DefaultVm = hint.Substring(3);
                }
                else if (hint.Contains("BNR="))//"BNR=01"
                {
                    lvColumn.CurrentScreenNo = ParseHelper.String2Int(hint.Substring(4));
                    existingLvNullable.ScreenNo = lvColumn.CurrentScreenNo;
                }

                //switch (hint.Length)
                //{
                //    case 5: // "VM=AN"
                //        lvColumn.DefaultVm = hint.Substring(3);
                //        break;
                //    case 6: //"BNR=01"
                //        lvColumn.CurrentScreenNo = ParseHelper.String2Int(hint.Substring(4));
                //        existingLvNullable.ScreenNo = lvColumn.CurrentScreenNo;
                //        break;
                //}

                // cut columnvalue to final text
                columnValue = columnValue.Remove(0, addHintLength);

                // recheck
                addHintPos = columnValue.IndexOf('$');
            }
            //if (addHintPos <= -1)
            //return columnValue;
            
            return columnValue;
        }

        /// <summary> check and parse for post comma digits </summary>
        /// <param name="sbRecvStr"></param>
        /// <param name="ds12Type"></param>
        /// <returns></returns>
        private int ParsePostComma(StringBuilder sbRecvStr, ItxListViewDs12Type ds12Type)
        {
            if (ds12Type != ItxListViewDs12Type.DateFieldD && ds12Type != ItxListViewDs12Type.TimeFieldZ)
                return ParseHelper.String2Int(sbRecvStr.ToString(3, 1));
            return 0;
        }

        /// <summary> parse the listview column type </summary>
        /// <param name="sbRecvStr"></param>
        /// <returns></returns>
        private ItxListViewDs12Type ParseColumnType(StringBuilder sbRecvStr)
        {
            switch (sbRecvStr[0])
            {
                case 'N':
                    return ItxListViewDs12Type.NumericN;
                case 'V':
                    return ItxListViewDs12Type.NumericWithSignV;
                case 'D':
                    return ItxListViewDs12Type.DateFieldD;
                case 'Z':
                    return ItxListViewDs12Type.TimeFieldZ;
                case 'I':
                    return ItxListViewDs12Type.NumericWithZeroWithoutCommaI;
                case 'S':
                    return ItxListViewDs12Type.NumericWithZeroWithoutCommaButSignAtTheEndS;
                case 'Y':
                    return ItxListViewDs12Type.CurrencyY;
                default:
                    return ItxListViewDs12Type.Unknown;
            }
        }

        /// <summary> parse an eventually received sort hint </summary>
        /// <param name="sortKz"></param>
        /// <param name="lvColumn"></param>
        private void ParseDs12SortKz(char sortKz, ItxListViewColumn lvColumn)
        {
            switch (sortKz)
            {
                case '+':
                    lvColumn.Ds12Sort = EItxSortMode.Ascending;
                    break;
                case '-':
                    lvColumn.Ds12Sort = EItxSortMode.Descending;
                    break;
                default:
                    lvColumn.Ds12Sort = EItxSortMode.None;
                    break;
            }
        }
    }
}